package org.jeecg.common.rc.aop.dicttrans.dictaop.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.core.CommonConstant;
import org.jeecg.common.rc.aop.dicttrans.classutil.ByteBuddyUtil;
import org.jeecg.common.rc.aop.dicttrans.dictaop.DictCode2Name;
import org.jeecg.common.rc.aop.dicttrans.dictaop.DictCode2Names;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.AfterObject;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.Describetor;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.TabMultiIn;
import org.jeecg.common.rc.aop.dicttrans.dictaop.entity.TransInfo;
import org.jeecg.common.util.RefUtil;
import org.jeecg.common.util.spelutil.SpELUtils;
import org.jeecg.common.util.stream_wrapper.StreamWrapper;
import org.jeecg.crud.sys.model.DictModel;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zjarlin
 * @since 2023/10/19 10:48
 */
//@Component
public class TransUtil {

    public static final String ROOT_OBJECT = "rootObject";
    public static final String OUT_VO = "outVO";
    //上下文前缀
    @Getter
    private static final Map<String, Object> spelContextMap = new ConcurrentHashMap<>();

    /** 单个实体解析为 List<TransInfo<></> */
    @SneakyThrows
    public static List<TransInfo<DictCode2Name>> process(Object in) {
        List<Describetor<DictCode2Name>> annoMap = RefUtil.findAnnoMap(in, DictCode2Name.class, DictCode2Names.class, DictCode2Names::value, true, true);
        List<TransInfo<DictCode2Name>> collect = annoMap.stream().flatMap(describetor -> {
            Object rootObject = describetor.getRootObject();
            String fieldName = describetor.getFieldName();
            List<DictCode2Name> needSearchAnnotations = describetor.getNeedSearchAnnotations();
            Stream<TransInfo<DictCode2Name>> stringStream = Stream.iterate(0, (i) -> ++i).limit(needSearchAnnotations.size()).map(i -> {
                DictCode2Name anno = needSearchAnnotations.get(i);
                String alias = anno.serializationAlias();
                String nameColumn = StrUtil.toCamelCase(anno.nameColumn());
                String other = fieldName + CommonConstant.DICT_TEXT_SUFFIX + (i == 0 ? "" : String.valueOf(i));
                String firstNonBlank = CharSequenceUtil.firstNonBlank(alias, nameColumn, other);
                TransInfo<DictCode2Name> transInfo1 = new TransInfo<>();
                BeanUtil.copyProperties(describetor, transInfo1);

                transInfo1.setAnno(anno);
                transInfo1.setTranslatedAttributeNames(firstNonBlank);
                transInfo1.setTranslatedType(anno.spelValueType());
                transInfo1.setAttributeNameBeforeTranslation(fieldName);
                transInfo1.setValueBeforeTranslation(describetor.getFieldValue());
                transInfo1.setClassificationOfTranslation(getTranslateType(transInfo1));
                return transInfo1;
            });
            return stringStream;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 0代表内置字典多翻译
     * 1内置字典单翻译
     * 2任意表多翻译
     * 3任意表单翻译
     * 4spel表达式
     * -1其他
     *
     * @param transInfo 入参
     * @return int
     * @author zjarlin
     * @since 2023/10/19
     */

    public static int getTranslateType(TransInfo<DictCode2Name> transInfo) {
        DictCode2Name anno = transInfo.getAnno();
        /** 翻译前的值 */
        Object valueBeforeTranslation = transInfo.getValueBeforeTranslation();
        String string = valueBeforeTranslation.toString();
        boolean isMulti = StrUtil.containsAny(string, ",");
        String dictCode = Optional.ofNullable(anno.dicCode()).filter(StrUtil::isNotBlank).orElse(anno.value());
        String tab = anno.tab();
        String codeColumn = anno.codeColumn();
        String nameColumn = anno.nameColumn();
        boolean isUseSysDefaultDict = StrUtil.isNotBlank(dictCode) && StrUtil.isAllBlank(tab, codeColumn, nameColumn);
        boolean useSpel = StrUtil.isNotBlank(anno.spelExp());

        //内置字典多翻译
        boolean one = isMulti && isUseSysDefaultDict && !useSpel;
        //内置字典但翻译
        boolean one1 = !isMulti && isUseSysDefaultDict && !useSpel;
        //任意表多翻译
        boolean one2 = isMulti && !isUseSysDefaultDict && !useSpel;
        //任意表但翻译
        boolean one3 = !isMulti && !isUseSysDefaultDict && !useSpel;

        int i = one ? 0 : one1 ? 1 : one2 ? 2 : one3 ? 3 : useSpel ? 4 : -1;
        return i;
    }

    public static List<ByteBuddyUtil.NeedAddInfo> getNeedAddFields(Object obj) {
        List<Describetor<DictCode2Name>> annoMap = RefUtil.findAnnoMap(obj, DictCode2Name.class, DictCode2Names.class, DictCode2Names::value, true, false);
        List<ByteBuddyUtil.NeedAddInfo> collect = annoMap.stream().flatMap(e -> {
            String fieldName = e.getFieldName();
            List<DictCode2Name> needSearchAnnotations = e.getNeedSearchAnnotations();
            return Stream.iterate(0, (i) -> ++i).limit(needSearchAnnotations.size()).map(i -> {
                DictCode2Name anno = needSearchAnnotations.get(i);
                String alias = anno.serializationAlias();
                String nameColumn = StrUtil.toCamelCase(anno.nameColumn());
                String other = fieldName + CommonConstant.DICT_TEXT_SUFFIX + (i == 0 ? "" : String.valueOf(i));
                String firstNonBlank = CharSequenceUtil.firstNonBlank(alias, nameColumn, other);
                Class<?> aClass1 = StrUtil.isNotBlank(anno.spelExp()) ? anno.spelValueType() : String.class;
                Object rootObject = e.getRootObject();
                boolean equals = Objects.equals(e.getFieldEnum(), RefUtil.FIELD_TYPE_T);
                boolean equals1 = RefUtil.FIELD_TYPE_COLL.equals(e.getFieldEnum());
                ByteBuddyUtil.NeedAddInfo needAddInfo = new ByteBuddyUtil.NeedAddInfo();
                needAddInfo.setRootObject(rootObject);
                needAddInfo.setFieldName(firstNonBlank);
                needAddInfo.setRecur(equals || equals1 ? true : false);
                needAddInfo.setIsT(equals);
                needAddInfo.setIsColl(equals1);
//                Type fieldType = e.getFieldType();
                needAddInfo.setType(aClass1);
                return needAddInfo;
            });
        }).collect(Collectors.toList());
        return collect;
    }

    /**使用字节码技术添加字段  */
    @SneakyThrows
    @NotNull
    public static AfterObject makeAddInfoObject(Object obj) {
        Class<?> aClass = obj.getClass();
        //寻找可重复注解
        List<Describetor<DictCode2Name>> annoMap = RefUtil.findAnnoMap(obj, DictCode2Name.class, DictCode2Names.class, DictCode2Names::value, true, true);
        //需要增加的字段
        AfterObject afterObject = new AfterObject();
        if (CollUtil.isEmpty(annoMap)) {
            afterObject.setAfterObject(obj);
            afterObject.setNeedAddField(null);
            afterObject.setDescribetors(null);
            return afterObject;
        }
        Set<Pair<String, ? extends Class<?>>> ret = new HashSet<>();
        Set<Pair<String, ? extends Class<?>>> collect1 = annoMap.stream()
                .filter(e -> Objects.nonNull(e.getSuperObject()))
                .map(field -> {
                    String fieldName = field.getFieldName();
                    Object rootObject = field.getRootObject();
                    Object fieldValue = ReflectUtil.getFieldValue(rootObject, fieldName);
                    AfterObject afterObject1 = makeAddInfoObject(fieldValue);
                    Object o = afterObject1.getAfterObject();
                    Class<?> aClass1 = o.getClass();
                    return Pair.of(fieldName, aClass1);
                }).collect(Collectors.toSet());
        ret.addAll(collect1);
        Set<Pair<String, ? extends Class<?>>> collect = annoMap.stream()
                .filter(e -> Objects.isNull(e.getSuperObject()))
                .flatMap(field -> {
                    String propertyName = field.getFieldName();
                    List<DictCode2Name> value = field.getNeedSearchAnnotations();
                    Stream<Pair<String, ? extends Class<?>>> pairStream = Stream.iterate(0, (i) -> ++i).limit(value.size()).map(i -> {
                        DictCode2Name anno = value.get(i);
                        String alias = anno.serializationAlias();
                        String nameColumn = StrUtil.toCamelCase(anno.nameColumn());
                        String other = propertyName + CommonConstant.DICT_TEXT_SUFFIX + (i == 0 ? "" : String.valueOf(i));
                        String firstNonBlank = CharSequenceUtil.firstNonBlank(alias, nameColumn, other);
                        Class<?> aClass1 = StrUtil.isNotBlank(anno.spelExp()) ? anno.spelValueType() : String.class;
                        return Pair.of(firstNonBlank, aClass1);
                    });
                    return pairStream;
                }).collect(Collectors.toSet());
        ret.addAll(collect);

        if (CollUtil.isNotEmpty(ret)) {
            Class<?> aClassProxy = ByteBuddyUtil.genChildClassWithPair(ret, aClass);
            Object o = aClassProxy.newInstance();
            BeanUtil.copyProperties(obj, o);
            afterObject.setAfterObject(o);
            afterObject.setNeedAddField(collect);
            afterObject.setDescribetors(annoMap);
        }
        return afterObject;

    }


    /**
     * 处理内置字典翻译
     */
    public static void processBuiltInDictionaryTranslation(Map<Integer, List<TransInfo<DictCode2Name>>> translateTypeListMap) {
        if (MapUtil.isEmpty(translateTypeListMap)) {
            return;
        }
        String dictCode = translateTypeListMap
                .entrySet().stream()
                .filter(e -> {
                    Integer translateType = e.getKey();
                    return translateType.equals(0)
                           ||
                           translateType.equals(1);
                })
                .flatMap(e -> {
                    List<TransInfo<DictCode2Name>> transInfos = e.getValue();
                    return transInfos.stream().map(x -> {
                        DictCode2Name anno = x.getAnno();
                        String value = anno.value();
                        return Optional.ofNullable(anno.dicCode()).filter(StrUtil::isNotBlank).orElse(value);
                    });
                })
                .distinct()
                .collect(Collectors.joining(","));
        String fieldRuntimeStrValue = translateTypeListMap.entrySet().stream()
                .filter(e -> {
                    Integer translateType = e.getKey();
                    return translateType.equals(0)
                           ||
                           translateType.equals(1);
                })
                .flatMap(e -> {
                    List<TransInfo<DictCode2Name>> transInfos = e.getValue();
//                    String dictCode = transInfos.stream().map(x -> x.getAnno().dicCode()).collect(Collectors.joining(","));
                    //                    Pair<String, String> of = Pair.of(dictCode, value);
                    Stream<String> stringStream = transInfos.stream().map(x -> x.getValueBeforeTranslation().toString());
                    return stringStream;
                })
                .distinct()
                .collect(Collectors.joining(","));
        Map<String, Object> stringObjectHashMap = getSpelContextMap();
        stringObjectHashMap.put("dictCode", dictCode);
        stringObjectHashMap.put("fieldRuntimeStrValue", fieldRuntimeStrValue);
        String dictMultiTranslator = DictCode2Name.dictMultiTranslator;
        Object o = stringObjectHashMap.get(ROOT_OBJECT);
//       rootObject
        Map<String, List<DictModel>> map = SpELUtils.evaluateExpression(o, stringObjectHashMap, dictMultiTranslator, Map.class);
        if (MapUtil.isEmpty(map)) {
            return;
        }
        translateTypeListMap.forEach((k, v) -> {
            v.forEach(e -> {
                Object rootObject = e.getRootObject();
                DictCode2Name anno = e.getAnno();
                String dicCode = Optional.ofNullable(anno.dicCode()).filter(StrUtil::isNotBlank).orElse(anno.value());
                List<DictModel> dictModels = map.get(dicCode);
                if (CollUtil.isEmpty(dictModels)) {
                    return;
                }
                /** 翻以前的值 */
                String flipPastValues = e.getValueBeforeTranslation().toString();
                boolean multi = StrUtil.containsAny(flipPastValues, ",");
                if (multi) {
                    List<String> split1 = StrUtil.split(flipPastValues, ",");
                    String collect = split1.stream().map(value -> {
                        DictModel one = StreamWrapper.lambdaquery(dictModels).eq(true, y -> y.getValue(), value).one();
                        String label = one.getLabel();
                        return label;
                    }).collect(Collectors.joining(","));
                    e.setTranslatedValue(collect);

                    e.setRootObjectHashBsm(rootObject.getClass().getSimpleName() + e.getTranslatedAttributeNames());
                    ReflectUtil.setFieldValue(rootObject, e.getTranslatedAttributeNames(), collect);
//                    return;
                }
                DictModel one = StreamWrapper.lambdaquery(dictModels).eq(true, y -> y.getValue(), flipPastValues).one();
                String label = one.getLabel();
                e.setTranslatedValue(label);
                e.setRootObjectHashBsm(rootObject.getClass().getSimpleName() + e.getTranslatedAttributeNames());
                ReflectUtil.setFieldValue(rootObject, e.getTranslatedAttributeNames(), label);
            });
        });
    }

    /**
     * 处理任意表翻译
     */
    public static void processAnyTableTranslation(Map<Integer, List<TransInfo<DictCode2Name>>> translateTypeListMap) {
        if (MapUtil.isEmpty(translateTypeListMap)) {
            return;
        }
        List<TransInfo<DictCode2Name>> collect = translateTypeListMap.entrySet().stream()
                .filter(e -> {
                    Integer translateType = e.getKey();
                    return translateType.equals(2)
                           ||
                           translateType.equals(3);
                })
                .flatMap(e -> {
                    return e.getValue().stream();
                })
                .collect(Collectors.toList());
        //按tab和code分组


        Map<Pair<String, String>, List<TransInfo<DictCode2Name>>> pairListMap = collect.stream().collect(Collectors.groupingBy(
                e -> {
                    DictCode2Name anno2 = e.getAnno();
                    String tab = anno2.tab();
                    String s = anno2.codeColumn();
                    return Pair.of(tab, s);
                }
        ));
        //多个
        pairListMap.entrySet().stream().map(e -> {
                    Pair<String, String> key = e.getKey();
                    String tab = key.getLeft();
                    String codeColumn = key.getRight();
                    List<TransInfo<DictCode2Name>> value = e.getValue();
                    String nameColumns = value.stream().map(x -> {
                                DictCode2Name anno = x.getAnno();
//                        String tab = anno.tab();
//                        String codeColumn = anno.codeColumn();
                                String s = anno.nameColumn();
//                        String value = x.get翻译前的值().toString();
                                return s;
                            })
                            .distinct()
                            .collect(Collectors.joining(","));
                    List<String> keys = value.stream().map(x -> {
//                                DictCode2Name anno = x.getAnno();
//                        String tab = anno.tab();
//                        String codeColumn = anno.codeColumn();
//                        String s = anno.nameColumn();
                                return x.getValueBeforeTranslation().toString();
                            })
                            .distinct()
                            .collect(Collectors.toList());
                    TabMultiIn tabMultiIn = new TabMultiIn();
                    tabMultiIn.setTab(tab);
                    tabMultiIn.setCode(codeColumn);
                    tabMultiIn.setName(nameColumns);
                    tabMultiIn.setKeys(keys);
                    return Pair.of(tabMultiIn, value);
                })
                .distinct()
                .forEach(e -> {
                    TabMultiIn tabMultiIn = e.getLeft();
                    List<TransInfo<DictCode2Name>> right = e.getRight();
                    String tab = tabMultiIn.getTab();
                    String nameColumn = tabMultiIn.getName();
                    String codeColumn = tabMultiIn.getCode();
                    List<String> fieldRuntimeStrValue = tabMultiIn.getKeys();
                    Map<String, Object> objectObjectHashMap = getSpelContextMap();
                    objectObjectHashMap.put("tab", tab);
                    objectObjectHashMap.put("nameColumn", nameColumn);
                    objectObjectHashMap.put("codeColumn", codeColumn);
                    objectObjectHashMap.put("fieldRuntimeStrValue", fieldRuntimeStrValue);
                    Object o = objectObjectHashMap.get(ROOT_OBJECT);
                    String tableMultiTranslator = DictCode2Name.tableMultiTranslator;
                    List<JSONObject> info = SpELUtils.evaluateExpression(o, objectObjectHashMap, tableMultiTranslator, List.class);
//                    right.
                    right.forEach(needSetInfo -> {
                        Object rootObject = needSetInfo.getRootObject();
                        Object afterObject = needSetInfo.getAfterObject();
                        /** 翻译后的名 */
                        String translatedName = needSetInfo.getTranslatedAttributeNames();
                        DictCode2Name anno = needSetInfo.getAnno();
                        String tab1 = anno.tab();
                        String codeColumn1 = anno.codeColumn();
                        String nameColumn1 = anno.nameColumn();
                        /** 翻译前的值 */
                        String valueBeforeTranslation = needSetInfo.getValueBeforeTranslation().toString();
                        boolean multi = valueBeforeTranslation.contains(",");
                        if (multi) {
                            List<String> split = StrUtil.split(valueBeforeTranslation, ",");
                            String collect1 = split.stream().map(singleValueBeforeTranslation -> {
                                JSONObject one = StreamWrapper.lambdaquery(info).eq(true, y -> y.getString(codeColumn1), singleValueBeforeTranslation).one();
                                String nameValue = one.getString(nameColumn1);
                                needSetInfo.setTranslatedValue(nameValue);
                                return nameValue;
                            }).collect(Collectors.joining(","));
                            needSetInfo.setTranslatedValue(collect1);
                            needSetInfo.setRootObjectHashBsm(rootObject.getClass().getSimpleName() + needSetInfo.getTranslatedAttributeNames());
                            ReflectUtil.setFieldValue(afterObject, translatedName, collect1);
//                            return;
                        }
//                        Object 翻译前的值 = string;
                        JSONObject one = StreamWrapper.lambdaquery(info)
//                                .eq(true, y -> y.getOcrString(nameColumn1), 翻译前的值)
                                .eq(true, y -> y.getString(codeColumn1), valueBeforeTranslation).one();
                        String nameValue = one.getString(nameColumn1);
                        needSetInfo.setTranslatedValue(nameValue);
                        needSetInfo.setRootObjectHashBsm(rootObject.getClass().getSimpleName() + needSetInfo.getTranslatedAttributeNames());
                        ReflectUtil.setFieldValue(rootObject, translatedName, nameValue);
                    });
                });
    }

    /**
     * 处理spel表达式
     */
    public static void processingSpelExpressions(Map<Integer, List<TransInfo<DictCode2Name>>> translateTypeListMap) {
        if (MapUtil.isEmpty(translateTypeListMap)) {
            return;
        }
        translateTypeListMap.entrySet().stream().filter(e -> e.getKey().equals(4)).flatMap(e -> e.getValue().stream()).forEach(e -> {
            Object rootObject = e.getRootObject();
            Object afterObject = e.getAfterObject();
            /** 翻译后的名 */
            String translatedAttributeNames = e.getTranslatedAttributeNames();
            DictCode2Name anno = e.getAnno();
            String s = anno.spelExp();
            Class<?> aClass = anno.spelValueType();
//            Object spelContextMapObject = getSpelContextMapObject();
            Map<String, Object> spelContextMap1 = getSpelContextMap();
            Object o = SpELUtils.evaluateExpression(rootObject, spelContextMap1, s, aClass);
            e.setTranslatedValue(o);
            e.setRootObjectHashBsm(rootObject.getClass().getSimpleName() + e.getTranslatedAttributeNames());
            ReflectUtil.setFieldValue(rootObject, translatedAttributeNames, o);
        });

    }

}
